import type { Fn } from '@knno/jsx';
import SVGArrowDown from '../../svg/chevron-down.svg';
import type { GridConfig } from '../grid/grid';
import { IconInput, type IconInputProps } from '../input/input';
import { type ColumnOption, getChildren, getText, getValue } from '../list/list';
import { PopList, type PopListRowType, type PopListSelectHandler } from '../popList/popList';
import { DATA } from '../symbol';

interface PopupResult {
	value: unknown;
	text: string;
}

type SelectData = {
	value: unknown;
	options: PopListRowType[];
	columnOption: ColumnOption;
	multiSelect: boolean;
	filterable: boolean;
	gridConfig?: Partial<GridConfig>;
	disableField: number | string | null;
	popupWidth?: number | string;
	readOnly: boolean;
	popup?: PopListSelectHandler;
};

export type SelectProps = IconInputProps<Select> & {
	popupWidth?: number | string;
	disableBy?: number | string;
	/**
	 * Whether to allow multiple selections.
	 * @default false
	 */
	multiSelect?: boolean;
	/**
	 * Whether to allow filtering.
	 * @default false
	 */
	filterable?: boolean;
	options?: PopListRowType[];
	/**
	 * 选择框数据配置
	 */
	column?: ColumnOption;
	selectedValue?: unknown;
	onPopup?: (this: Select) => Promise<PopupResult | null>;
	onSelectChange?: (this: Select, value: unknown) => void;
};

export class Select extends IconInput<SelectProps, Select> {
	private [DATA]: SelectData;
	constructor(props: SelectProps) {
		super(props);
		this[DATA] = {
			readOnly: props.readOnly ?? false,
			value: props.selectedValue ?? null,
			options: props.options ?? [],
			filterable: props.filterable ?? false,
			multiSelect: props.multiSelect ?? false,
			columnOption: props.column ?? { text: 'text', value: 'value' },
			popupWidth: props.popupWidth ?? undefined,
			gridConfig: undefined,
			disableField: props.disableBy ?? null,
		};
		super.setSuffixIcon(SVGArrowDown);
		super.setReadOnly(true);
		this.el.addEventListener('click', () => {
			this.openSelect();
		});
		this.el.addEventListener('keyup', (evt) => {
			if (evt.key === 'Space' || evt.key === ' ') {
				this.openSelect();
			}
		});
		super.el.classList.add('select');
		if (props.onClear) this.set('onClear', props.onClear);
	}

	setConfig(gridConfig: Partial<GridConfig>) {
		this[DATA].gridConfig = gridConfig;
	}

	private setText(): void {
		const find = (arr: unknown[]): boolean => {
			for (let i = 0; i < arr.length; i++) {
				const row = arr[i];
				if (getValue(row, this[DATA].columnOption) === this[DATA].value) {
					super.setValue(getText(row, this[DATA].columnOption));
					return true;
				} else if (typeof row === 'object' && row !== null) {
					const children = getChildren(row, this[DATA].columnOption);
					if (children && find(children)) {
						return true;
					}
				}
			}
			return false;
		};
		find(this[DATA].options);
	}

	clear(): this {
		this.setSelectedValue(null);
		return this;
	}

	openSelect() {
		if (this.readOnly || this.disabled) return;
		if (this.props.onPopup) {
			this.props.onPopup
				?.call(this)
				.then((result) => {
					if (result) {
						const oldValue = this.selectedValue;
						this.setSelectedValue(result.value);
						super.setValue(result.text);
						if (oldValue !== result.value) {
							this.setCustomValidity('');
							this.emitChange(result.value);
						}
					}
					this.focus();
				})
				.catch((reason) => {
					console.error(reason);
				});
			return;
		}
		if (this[DATA].popup) {
			return;
		}
		this[DATA].popup = PopList.show(this[DATA].options, {
			columnOption: this[DATA].columnOption,
			refer: this.el,
			focusOnClose: this.input,
			activeValue: this.selectedValue,
			multiSelect: this.multiSelect,
			useAction: true,
			useSearch: this.filterable,
			gridConfig: this[DATA].gridConfig,
			focus: true,
			disableField: this[DATA].disableField,
		})
			.onSelect((value) => {
				const oldValue = this.selectedValue;
				this.setSelectedValue(value);
				this.input.focus();
				if (oldValue !== value) {
					this.setCustomValidity('');
					this.emitChange(value);
				}
			})
			.onClose(() => {
				this[DATA].popup = undefined;
			});
	}

	get column(): ColumnOption {
		return this[DATA].columnOption;
	}
	setColumn(columnOption: ColumnOption): this {
		this[DATA].columnOption = columnOption;
		if (this[DATA].columnOption.text === undefined) {
			this[DATA].columnOption.text = 'text';
		}
		if (this[DATA].columnOption.value === undefined) {
			this[DATA].columnOption.value = 'value';
		}
		this.setText();
		return this;
	}

	get multiSelect(): boolean {
		return this[DATA].multiSelect;
	}
	setMultiSelect(value: boolean): this {
		this[DATA].multiSelect = value;
		return this;
	}

	get filterable(): boolean {
		return this[DATA].filterable;
	}
	setFilterable(value: boolean): this {
		this[DATA].filterable = value;
		return this;
	}

	get options(): PopListRowType[] {
		return this[DATA].options;
	}
	setOptions(rows: PopListRowType[]): this {
		this[DATA].options = rows;
		this.setText();
		return this;
	}

	/**
	 * Disable some row by field value
	 */
	get disableBy(): string | number | null {
		return this[DATA].disableField;
	}
	setDisableBy(value: string | number | null): this {
		this[DATA].disableField = value;
		return this;
	}

	setPopupWidth(value: string | number): this {
		this[DATA].popupWidth = value;
		return this;
	}

	get selectedValue(): unknown {
		return this[DATA].value;
	}
	setSelectedValue(value: unknown): this {
		this[DATA].value = value;
		this.setText();
		return this;
	}

	override setValue(_: string): this {
		return this;
	}
	override setInputMode(_: string): this {
		return this;
	}
	override setAutoComplete(_: string): this {
		return this;
	}
	override setMaxLength(_: number): this {
		return this;
	}
	override setMinLength(_: number): this {
		return this;
	}
	override setPattern(_: string): this {
		return this;
	}
	override setType(_: string): this {
		return this;
	}
	override get readOnly(): boolean {
		return this[DATA].readOnly;
	}
	override setReadOnly(value: boolean): this {
		this[DATA].readOnly = value;
		return this;
	}
	override set<
		K extends string | number,
		S = K extends keyof Fn<SelectProps> ? SelectProps[K] : never,
	>(key: K, value: S): this;
	override set<K extends keyof Fn<SelectProps>>(key: K, value: SelectProps[K]): this;
	override set<K extends keyof Fn<SelectProps>>(key: K, value: SelectProps[K]): this {
		if (key === 'onClear') {
			super.set('onClear', () => {
				this.setSelectedValue(null);
				(value as Fn<SelectProps>['onClear'])?.call(this);
			});
			return this;
		} else return super.set(key, value);
	}

	private emitChange(value: unknown) {
		this.el.dispatchEvent(new Event('change', { bubbles: true }));
		this.props.onSelectChange?.call(this, value);
	}
}
